package com.example.whereismystuff.classes;

import java.io.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Hashtable;
import java.util.Stack;

public class ItemDataManager implements Manager<Item> 
{
	private static ItemDataManager m_ItemDataManager = new ItemDataManager();
	
	private File m_file;
	
	private int m_nItemIDNext = 0;
	
	private Hashtable<String, Integer> m_htItemNames;
	private Hashtable<Integer, Integer> m_htItemIDs;
	private ArrayList<Item> m_alItems;
	
	private boolean bConstructed = false;
	
	protected ItemDataManager()
	{
		m_htItemNames = new Hashtable<String, Integer>();
		m_htItemIDs = new Hashtable<Integer, Integer>();
		m_alItems = new ArrayList<Item>(10);
	}
	
	public static ItemDataManager getItemDataManager()
	{
		return m_ItemDataManager;
	}
	
	/**
	 * Add the Item to the database
	 * @param Item
	 */
	@Override
	public void addToDatabase(Item item)
	{
		if (getItem(item.getName()) != null)
			return;
		
		item.setUniqueID(m_nItemIDNext);
		++m_nItemIDNext;
		
		m_htItemNames.put(item.getName(), m_alItems.size());
		m_htItemIDs.put(item.getUniqueID(), m_alItems.size());
		
		m_alItems.add(item);
		
		WriteDataToDisk();
		
		UserDataManager udmDataManager = UserDataManager.getUserDataManager();
		String strUsername = item.getPosterName();
		
		User tempUser = udmDataManager.getUser(strUsername);
		tempUser.addItem(item);
		
		udmDataManager.WriteDataToDisk();
	}
	
	/**
	 * Removes an Item from the database.
	 * @param Item
	 * @return Item that was removed
	 */
	@Override
	public Item removeFromDatabase(Item item)
	{
		int nIndex = m_alItems.indexOf(item);
		if (nIndex == -1)
			return null;
		
		for (int n = nIndex+1; n < m_alItems.size(); ++n)
		{
			Item loopItem = m_alItems.get(n);
			Integer nPreviousSpot = m_htItemNames.get(loopItem.getName());
			m_htItemNames.put(loopItem.getName(), nPreviousSpot-1);
			m_htItemIDs.put(loopItem.getUniqueID(), nPreviousSpot-1);
		}
		
		Item removedItem = m_alItems.get(nIndex);
		User user = removedItem.getPoster();
		user.removeItem(removedItem);
		
		m_alItems.remove(nIndex);
		
		WriteDataToDisk();
		
		return removedItem;
	}
	
	/*public int AssociateItemWithUser(int nItemID, int nUserID)
	{
		Item item = getItem(nItemID);
		
		if (item == null)
			return -1; // RETURN CODE: Failed, I'll make it better in a bit
		if (UserDataManager.getUserDataManager().getUser(nUserID) == null)
			return -2; // RETURN CODE: Failed, I'll make it better in a bit
		
		item.setPosterID(nUserID);
		
		return 0;
	}*/
	
	/**
	 * Gets the item that is being searched for.
	 * Returns null if doesn't exist.
	 * @param strItemName
	 * @return Item
	 */
	public Item getItem(String strItemName)
	{
		Integer nTemp = m_htItemNames.get(strItemName);
		//System.out.println(m_htItemNames.get(strItemName));
		
		if (nTemp == null)
			return null;
		else
			return m_alItems.get(nTemp);
	}
	
	public Item getItem(int nID)
	{
		Integer nTemp = m_htItemIDs.get(nID);
		
		if (nTemp == null)
			return null;
		else
			return m_alItems.get(nID);
	}
	
	/**
	 * Will sort the database.
	 * Method might not be implemented.
	 */
	public void sort()
	{
	}
	
	public void WriteDataToDisk()
	{
		(new WriteFile()).Write();
	}
	
	private void PopulateItemDatabase()
	{
		(new ReadFile()).Read();
	}

	@Override
	public void updateDatabase(int nUniqueID, Item item)
	{
		Item tempItem = m_alItems.get(m_htItemIDs.get(nUniqueID));
		if (tempItem == null)
			return;
		
		tempItem.setName(item.getName());
		//set other things... in a bit
		
		WriteDataToDisk();
	}
	
	public interface DelegateReader
	{
		public String Invoke(String strCharSequence, Scanner scanner);
	}
	public interface DelegateWriter
	{
		public void Invoke(String strCharSequence, PrintWriter outputWriter);
	}
	
	private class ReadFile
	{
		private boolean bClaimed = false, bFound = false;
		private int nReward = 0;
		private String strCity, strState, strDescription, strName;
		private int nZipCode;
		private User poster, claimer;
		private String[] strTags;
		private int nUniqueID, nPosterID;
		private Stack<DelegateReader> m_readStack = new Stack<DelegateReader>();
		
		public void Read()
		{
			m_readStack.add(new ReadFileRoot());
			try
			{
				FileReader fileReader = new FileReader(m_file.getAbsoluteFile());
				BufferedReader outputReader = new BufferedReader(fileReader);
				String strLine;
				String strCharSequence;
				
				for (strLine = outputReader.readLine(); strLine != null; strLine = outputReader.readLine())
				{
					Scanner scanner = new Scanner(strLine);
					try
					{
						strCharSequence = scanner.next();
					}
					catch (NoSuchElementException endOfLine)
					{
						continue;
					}
					while (strCharSequence != null)
					{
						try
						{
							strCharSequence = m_readStack.peek().Invoke(strCharSequence, scanner);
						}
						catch (NoSuchElementException endOfLine)
						{
							break;
						}
					}
				}
				
				m_readStack.pop();
				outputReader.close();
			}
			catch (IOException error)
			{
				System.out.println("Error: " + error.getMessage());
				error.printStackTrace();
			}
		}
		
		private class ReadFileRoot implements DelegateReader
		{
			public String Invoke(String strCharSequence, Scanner scanner)
			{
				if (strCharSequence.equals("$IDNext"))
				{
					strCharSequence = scanner.next().trim();
					m_nItemIDNext = Integer.parseInt(strCharSequence);
				}
				else if (strCharSequence.equals("$Item"))
				{
					scanner.useDelimiter("line.seperator");
					strCharSequence = strName = scanner.next().trim();
				}
				else if (strCharSequence.equals("$Claimed"))
				{	
					strCharSequence = scanner.next();
					if (strCharSequence.equals("true"))
						bClaimed = true;
					else
						bClaimed = false;
				}
				else if (strCharSequence.equals("$Found"))
				{
					strCharSequence = scanner.next();
					if (strCharSequence.equals("true"))
						bFound = true;
					else
						bFound = false;
				}
				else if (strCharSequence.equals("$Reward"))
				{
					strCharSequence = scanner.next();
					// ignore for now
				}
				else if (strCharSequence.equals("$City"))
				{
					scanner.useDelimiter("line.seperator");
					strCity = strCharSequence = scanner.next().trim();
				}
				else if (strCharSequence.equals("$State"))
				{
					scanner.useDelimiter("line.seperator");
					strState = strCharSequence = scanner.next().trim();
				}
				else if (strCharSequence.equals("$Description"))
				{
					scanner.useDelimiter("line.seperator");
					strCharSequence = scanner.next().trim();
					strDescription = strCharSequence.replace("${newline}", "\n");
				}
				else if (strCharSequence.equals("$Tags"))
				{
					m_readStack.add(new ReadFileTags());
				}
				else if (strCharSequence.equals("$ZipCode"))
				{
					strCharSequence = scanner.next();
					nZipCode = Integer.parseInt(strCharSequence);
				}
				else if (strCharSequence.equals("$PosterID"))
				{
					strCharSequence = scanner.next();
					nPosterID = Integer.parseInt(strCharSequence);
				}
				else if (strCharSequence.equals("}"))
				{
					Item newItem = new Item(strName, strDescription, strCity, strState, nZipCode, nPosterID, strTags);
					newItem.setUniqueID(m_alItems.size());
					m_alItems.add(newItem);
					m_htItemNames.put(newItem.getName(), m_alItems.size()-1);
					m_htItemIDs.put(newItem.getUniqueID(), m_alItems.size()-1);
				}
				
				return scanner.next();
			}
		}
		
		private class ReadFileTags implements DelegateReader
		{
			private String[] strTempTags = new String[10];
			private int nSizeTempTags = 0;
			public String Invoke(String strCharSequence, Scanner scanner)
			{
				if (strCharSequence.equals("}"))
				{
					strTags = OutputTags();
					m_readStack.pop();
				}
				else if (strCharSequence.equals("{"))
				{
					// Do Nothing
				}
				else
				{
					if (nSizeTempTags+1 >= strTempTags.length)
						ResizeTags();
					strTempTags[nSizeTempTags] = strCharSequence.trim();
					++nSizeTempTags;
				}
				
				return scanner.next();
			}
			
			private void ResizeTags()
			{
				String[] strTemp = new String[strTempTags.length*2];
				for (int n = 0; n < strTempTags.length; ++n)
					strTemp[n] = strTempTags[n];
				strTempTags = strTemp;
			}
			
			private String[] OutputTags()
			{
				String[] strOutputTags = new String[nSizeTempTags];
				for (int n = 0; n < strOutputTags.length; ++n)
					strOutputTags[n] = strTempTags[n];
				return strOutputTags;
			}
		}
	}
	
	private class WriteFile
	{
		public void Write()
		{
			String strIndentString = "";
		
			try
			{
				FileWriter m_fileStream = new FileWriter(m_file);
				BufferedWriter m_bufferedWriter = new BufferedWriter(m_fileStream);
				PrintWriter outputWriter = new PrintWriter(m_bufferedWriter);
				
				outputWriter.println("$Date " + Calendar.getInstance().getTime());
				outputWriter.println("$IDNext " + m_nItemIDNext);
				outputWriter.println();
				
				for (int n = 0; n < m_alItems.size(); ++n)
				{
					Item loopItem = m_alItems.get(n);
					outputWriter.println(strIndentString + "$Item " + loopItem.getName());
					outputWriter.println(strIndentString + "{");
					strIndentString = "\t";
					outputWriter.println(strIndentString + "$Claimed " + ((loopItem.isClaimed()) ? "true" : "false"));
					outputWriter.println(strIndentString + "$Found " + ((loopItem.isFound()) ? "true" : "false"));
					outputWriter.println(strIndentString + "$City " + loopItem.getCity());
					outputWriter.println(strIndentString + "$State " + loopItem.getState());
					outputWriter.println(strIndentString + "$ZipCode " + loopItem.getZipCode());
					outputWriter.println(strIndentString + "$Description " + loopItem.getDescription().replace("\n", "${newline}"));
					//outputWriter.println(strIndentString + "$PosterID " + loopItem.getPosterID());
					outputWriter.print(strIndentString + "$Tags { ");
					String[] loopTags = loopItem.getTags();
					for (int m = 0; m < loopTags.length; ++m)
						outputWriter.print(loopTags[m] + " ");
					outputWriter.println("}");
					outputWriter.println(strIndentString + "$ID " + loopItem.getUniqueID());
					strIndentString = "";
					outputWriter.println(strIndentString + "}");
					outputWriter.println();
				}
				
				outputWriter.close();
			}
			catch (IOException error)
			{
				System.out.println(error.getMessage());
				error.printStackTrace();
			}
		}
	}
	
	public String toString()
	{
		return "Items: " + m_alItems.toString() + "\nItemNames: " + m_htItemNames.toString() + "\nItemIDs: " + m_htItemIDs.toString();
	}
	
	public int getDatabaseSize()
	{
		return m_alItems.size();
	}
	
	public void Initialize(File file)
	{
		m_file = file;
		
		PopulateItemDatabase();
	}
}
